home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / hplip / scan / sane.py < prev    next >
Text File  |  2009-10-09  |  21KB  |  634 lines

  1. # -*- coding: utf-8 -*-
  2. #
  3. # (c) Copyright 2003-2008 Hewlett-Packard Development Company, L.P.
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  18. #
  19. # Based on:
  20. # "sane.py", part of the Python Imaging Library (PIL)
  21. # http://www.pythonware.com/products/pil/
  22. # Python wrapper on top of the _sane module, which is in turn a very
  23. # thin wrapper on top of the SANE library.  For a complete understanding
  24. # of SANE, consult the documentation at the SANE home page:
  25. # http://www.mostang.com/sane/ .#
  26. #
  27. # Modified to work without PIL by Don Welch
  28. #
  29. # (C) Copyright 2003 A.M. Kuchling.  All Rights Reserved
  30. # (C) Copyright 2004 A.M. Kuchling, Ralph Heinkel  All Rights Reserved
  31. #
  32. # Permission to use, copy, modify, and distribute this software and its
  33. # documentation for any purpose and without fee is hereby granted,
  34. # provided that the above copyright notice appear in all copies and that
  35. # both that copyright notice and this permission notice appear in
  36. # supporting documentation, and that the name of A.M. Kuchling and
  37. # Ralph Heinkel not be used in advertising or publicity pertaining to
  38. # distribution of the software without specific, written prior permission.
  39. #
  40. # A.M. KUCHLING, R.H. HEINKEL DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
  41. # SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
  42. # IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  43. # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
  44. # USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  45. # OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  46. # PERFORMANCE OF THIS SOFTWARE.
  47. # Python wrapper on top of the scanext module, which is in turn a very
  48. # thin wrapper on top of the SANE library.  For a complete understanding
  49. # of SANE, consult the documentation at the SANE home page:
  50. # http://www.mostang.com/sane/ .
  51. #
  52. # Original authors: Andrew Kuchling, Ralph Heinkel
  53. # Modified by: Don Welch
  54. #
  55.  
  56. # Std Lib
  57. import scanext
  58. import threading
  59. import time
  60. import os
  61. import Queue
  62.  
  63. # Local
  64. from base.g import *
  65. from base import utils
  66.  
  67. EVENT_SCAN_CANCELED = 1
  68.  
  69. TYPE_STR = { scanext.TYPE_BOOL:   "TYPE_BOOL",   scanext.TYPE_INT:    "TYPE_INT",
  70.              scanext.TYPE_FIXED:  "TYPE_FIXED",  scanext.TYPE_STRING: "TYPE_STRING",
  71.              scanext.TYPE_BUTTON: "TYPE_BUTTON", scanext.TYPE_GROUP:  "TYPE_GROUP" }
  72.  
  73. UNIT_STR = { scanext.UNIT_NONE:        "UNIT_NONE",
  74.              scanext.UNIT_PIXEL:       "UNIT_PIXEL",
  75.              scanext.UNIT_BIT:         "UNIT_BIT",
  76.              scanext.UNIT_MM:          "UNIT_MM",
  77.              scanext.UNIT_DPI:         "UNIT_DPI",
  78.              scanext.UNIT_PERCENT:     "UNIT_PERCENT",
  79.              scanext.UNIT_MICROSECOND: "UNIT_MICROSECOND" }
  80.  
  81.  
  82.  
  83.  
  84. class Option:
  85.     """Class representing a SANE option.
  86.     Attributes:
  87.     index -- number from 0 to n, giving the option number
  88.     name -- a string uniquely identifying the option
  89.     title -- single-line string containing a title for the option
  90.     desc -- a long string describing the option; useful as a help message
  91.     type -- type of this option.  Possible values: TYPE_BOOL,
  92.             TYPE_INT, TYPE_STRING, and so forth.
  93.     unit -- units of this option.  Possible values: UNIT_NONE,
  94.             UNIT_PIXEL, etc.
  95.     size -- size of the value in bytes
  96.     cap -- capabilities available; CAP_EMULATED, CAP_SOFT_SELECT, etc.
  97.     constraint -- constraint on values.  Possible values:
  98.         None : No constraint
  99.         (min,max,step)  Integer values, from min to max, stepping by
  100.         list of integers or strings: only the listed values are allowed
  101.     """
  102.  
  103.     def __init__(self, args, cur_device):
  104.         import string
  105.         self.cur_device = cur_device
  106.  
  107.         self.index, self.name, self.title, self.desc, self.type, \
  108.             self.unit, self.size, self.cap, self.constraint = args
  109.  
  110.         if type(self.name) != type(''):
  111.             self.name = str(self.name)
  112.  
  113.     def isActive(self):
  114.         return scanext.isOptionActive(self.cap)
  115.  
  116.     def isSettable(self):
  117.         return scanext.isOptionSettable(self.cap)
  118.  
  119.     def __repr__(self):
  120.         if self.isSettable():
  121.             settable = 'yes'
  122.         else:
  123.             settable = 'no'
  124.  
  125.         if self.isActive():
  126.             active = 'yes'
  127.             curValue = self.cur_device.getOption(self.name)
  128.         else:
  129.             active = 'no'
  130.             curValue = '<not available, inactive option>'
  131.  
  132.  
  133.         return """\nName:      %s
  134. Cur value: %s
  135. Index:     %d
  136. Title:     %s
  137. Desc:      %s
  138. Type:      %s
  139. Unit:      %s
  140. Constr:    %s
  141. isActive:    %s
  142. isSettable:  %s\n""" % (self.name, curValue,
  143.                       self.index, self.title, self.desc,
  144.                       TYPE_STR[self.type], UNIT_STR[self.unit],
  145.                       self.constraint, active, settable)
  146.         return s
  147.  
  148.     def limitAndSet(self, value):
  149.         if value is not None and self.constraint is not None:
  150.             if type(self.constraint) == type(()):
  151.                 if value < self.constraint[0]:
  152.                     value = self.constraint[0]
  153.                     log.warn("Invalid value for %s (%s < min value of %d). Using %d." %
  154.                         (self.name, self.name, value, value))
  155.  
  156.                 elif value > self.constraint[1]:
  157.                     value = self.constraint[1]
  158.                     log.warn("Invalid value for %s (%s > max value of %d). Using %d." %
  159.                         (self.name, self.name, value, value))
  160.  
  161.                 self.cur_device.setOption(self.name, value)
  162.  
  163.             elif type(self.constraint) == type([]):
  164.                 if value not in self.constraint:
  165.                     v = self.constraint[0]
  166.                     min_dist = sys.maxint
  167.                     for x in self.constraint:
  168.                         if abs(value-x) < min_dist:
  169.                             min_dist = abs(value-x)
  170.                             v = x
  171.  
  172.                     log.warn("Invalid value for %s (%s not in constraint list: %s). Using %d." %
  173.                         (self.name, self.name, value, ', '.join(self.constraint), v))
  174.  
  175.                     self.cur_device.setOption(self.name, v)
  176.  
  177.         else:
  178.             value = self.cur_device.getOption(self.name)
  179.  
  180.         return value
  181.  
  182.  
  183. ##class _SaneIterator:
  184. ##    """ intended for ADF scans.
  185. ##    """
  186. ##
  187. ##    def __init__(self, cur_device):
  188. ##        self.cur_device = cur_device
  189. ##
  190. ##    def __iter__(self):
  191. ##        return self
  192. ##
  193. ##    def __del__(self):
  194. ##        self.cur_device.cancelScan()
  195. ##
  196. ##    def next(self):
  197. ##        try:
  198. ##            self.cur_device.startScan()
  199. ##        except error, v:
  200. ##            if v == 'Document feeder out of documents':
  201. ##                raise StopIteration
  202. ##            else:
  203. ##                raise
  204. ##        return self.cur_device.performScan(1)
  205.  
  206.  
  207.  
  208.  
  209. class ScanDevice:
  210.     """Class representing a SANE device.
  211.     Methods:
  212.     startScan()    -- initiate a scan, using the current settings
  213.     cancelScan()   -- cancel an in-progress scanning operation
  214.  
  215.     Also available, but rather low-level:
  216.     getParameters() -- get the current parameter settings of the device
  217.     getOptions()    -- return a list of tuples describing all the options.
  218.  
  219.     Attributes:
  220.     optlist -- list of option names
  221.  
  222.     You can also access an option name to retrieve its value, and to
  223.     set it.  For example, if one option has a .name attribute of
  224.     imagemode, and scanner is a ScanDevice object, you can do:
  225.          print scanner.imagemode
  226.          scanner.imagemode = 'Full frame'
  227.          scanner.['imagemode'] returns the corresponding Option object.
  228.     """
  229.  
  230.     def __init__(self, dev):
  231.         self.scan_thread = None
  232.         self.dev = scanext.openDevice(dev)
  233.         self.options = {}
  234.         self.__load_options_dict()
  235.  
  236.  
  237.     def __load_options_dict(self):
  238.         opts = self.options
  239.         opt_list = self.dev.getOptions()
  240.  
  241.         for t in opt_list:
  242.             o = Option(t, self)
  243.  
  244.             if o.type != scanext.TYPE_GROUP:
  245.                 opts[o.name] = o
  246.  
  247.  
  248.     def setOption(self, key, value):
  249.         opts = self.options
  250.  
  251.         if key not in opts:
  252.             opts[key] = value
  253.             return
  254.  
  255.         opt = opts[key]
  256.  
  257.         if opt.type == scanext.TYPE_GROUP:
  258.             log.error("Groups can't be set: %s" % key)
  259.  
  260.         if not scanext.isOptionActive(opt.cap):
  261.             log.error("Inactive option: %s" % key)
  262.  
  263.         if not scanext.isOptionSettable(opt.cap):
  264.             log.error("Option can't be set by software: %s" % key)
  265.  
  266.         if type(value) == int and opt.type == scanext.TYPE_FIXED:
  267.             # avoid annoying errors of backend if int is given instead float:
  268.             value = float(value)
  269.  
  270.         try:
  271.             self.last_opt = self.dev.setOption(opt.index, value)
  272.         except scanext.error:
  273.             log.error("Unable to set option %s to value %s" % (key, value))
  274.  
  275.         # do binary AND to find if we have to reload options:
  276.         if self.last_opt & scanext.INFO_RELOAD_OPTIONS:
  277.             self.__load_options_dict()
  278.  
  279.  
  280.     def getOption(self, key):
  281.         opts = self.options
  282.  
  283.         if key == 'optlist':
  284.             return opts.keys()
  285.  
  286.         if key == 'area':
  287.             return (opts["tl-x"], opts["tl-y"]), (opts["br-x"], opts["br-y"])
  288.  
  289.         if key not in opts:
  290.             raise AttributeError, 'No such attribute: %s' % key
  291.  
  292.         opt = opts[key]
  293.  
  294.         if opt.type == scanext.TYPE_BUTTON:
  295.             raise AttributeError, "Buttons don't have values: %s" % key
  296.  
  297.         if opt.type == scanext.TYPE_GROUP:
  298.             raise AttributeError, "Groups don't have values: %s " % key
  299.  
  300.         if not scanext.isOptionActive(opt.cap):
  301.             raise AttributeError, 'Inactive option: %s' % key
  302.  
  303.         return self.dev.getOption(opt.index)
  304.  
  305.  
  306.     def getOptionObj(self, key):
  307.         opts = self.options
  308.         if key in opts:
  309.             return opts[key]
  310.  
  311.  
  312.     def getParameters(self):
  313.         """Return a 6-tuple holding all the current device settings:
  314.            (format, format_name, last_frame, (pixels_per_line, lines), depth, bytes_per_line)
  315.  
  316.             - format is the SANE frame type
  317.             - format is one of 'grey', 'color' (RGB), 'red', 'green', 'blue'.
  318.             - last_frame [bool] indicates if this is the last frame of a multi frame image
  319.             - (pixels_per_line, lines) specifies the size of the scanned image (x,y)
  320.             - lines denotes the number of scanlines per frame
  321.             - depth gives number of pixels per sample
  322.         """
  323.         return self.dev.getParameters()
  324.  
  325.  
  326.     def getOptions(self):
  327.         "Return a list of tuples describing all the available options"
  328.         return self.dev.getOptions()
  329.  
  330.  
  331.     def startScan(self, byte_format='BGRA', update_queue=None, event_queue=None):
  332.         """
  333.             Perform a scan with the current device.
  334.             Calls sane_start().
  335.         """
  336.         if not self.isScanActive():
  337.             status = self.dev.startScan()
  338.             self.format, self.format_name, self.last_frame, self.pixels_per_line, \
  339.             self.lines, self.depth, self.bytes_per_line = self.dev.getParameters()
  340.  
  341.             self.scan_thread = ScanThread(self.dev, byte_format, update_queue, event_queue)
  342.             self.scan_thread.scan_active = True
  343.             self.scan_thread.start()
  344.             return True, self.lines * self.bytes_per_line, status
  345.         else:
  346.             # Already active
  347.             return False, 0, scanext.SANE_STATUS_DEVICE_BUSY
  348.  
  349.  
  350.     def cancelScan(self):
  351.         "Cancel an in-progress scanning operation."
  352.         return self.dev.cancelScan()
  353.  
  354.  
  355.     def getScan(self):
  356.         "Get the output buffer and info about a completed scan."
  357.         if not self.isScanActive():
  358.             s = self.scan_thread
  359.  
  360.             return s.buffer, s.format, s.format_name, s.pixels_per_line, \
  361.                 s.lines, s.depth, s.bytes_per_line, s.pad_bytes, s.total_read
  362.  
  363.  
  364.     def freeScan(self):
  365.         "Cleanup the scan file after a completed scan."
  366.         if not self.isScanActive():
  367.             s = self.scan_thread
  368.  
  369.             try:
  370.                 s.buffer.close()
  371.                 os.remove(s.buffer_path)
  372.             except (IOError, AttributeError):
  373.                 pass
  374.  
  375.  
  376.     def isScanActive(self):
  377.         if self.scan_thread is not None:
  378.             return self.scan_thread.isAlive() and self.scan_thread.scan_active
  379.         else:
  380.             return False
  381.  
  382.  
  383.     def waitForScanDone(self):
  384.         if self.scan_thread is not None and \
  385.             self.scan_thread.isAlive() and \
  386.             self.scan_thread.scan_active:
  387.  
  388.             try:
  389.                 self.scan_thread.join()
  390.             except KeyboardInterrupt:
  391.                 pass
  392.  
  393.  
  394.     def waitForScanActive(self):
  395.         time.sleep(0.5)
  396.         if self.scan_thread is not None:
  397.             while True:
  398.                 #print self.scan_thread.isAlive()
  399.                 #print self.scan_thread.scan_active
  400.                 if self.scan_thread.isAlive() and \
  401.                     self.scan_thread.scan_active:
  402.                     return
  403.  
  404.                 time.sleep(0.5)
  405.                 #print "Waiting..."
  406.  
  407.  
  408. ##    def scanMulti(self):
  409. ##        return _SaneIterator(self)
  410.  
  411.  
  412.     def closeScan(self):
  413.         "Close the SANE device after a scan."
  414.         self.dev.closeScan()
  415.  
  416.  
  417.  
  418. class ScanThread(threading.Thread):
  419.     def __init__(self, device, byte_format='BGRA', update_queue=None, event_queue=None):
  420.         threading.Thread.__init__(self)
  421.         self.scan_active = True
  422.         self.dev = device
  423.         self.update_queue = update_queue
  424.         self.event_queue = event_queue
  425.         self.buffer_fd, self.buffer_path = utils.make_temp_file(prefix='hpscan')
  426.         self.buffer = os.fdopen(self.buffer_fd, "w+b")
  427.         self.format = -1
  428.         self.format_name = ''
  429.         self.last_frame = -1
  430.         self.pixels_per_line = -1
  431.         self.lines = -1
  432.         self.depth = -1
  433.         self.bytes_per_line = -1
  434.         self.pad_bytes = -1
  435.         self.total_read = 0
  436.         self.byte_format = byte_format
  437.  
  438.  
  439.     def updateQueue(self, status, bytes_read):
  440.         if self.update_queue is not None:
  441.             try:
  442.                 status = int(status)
  443.             except (ValueError, TypeError):
  444.                 status = -1 #scanext.SANE_STATUS_GOOD
  445.  
  446.             self.update_queue.put((status, bytes_read))
  447.             time.sleep(0)
  448.  
  449.  
  450.     def run(self):
  451.         #self.scan_active = True
  452.         self.format, self.format_name, self.last_frame, self.pixels_per_line, \
  453.             self.lines, self.depth, self.bytes_per_line = self.dev.getParameters()
  454.  
  455.         log.debug("format=%d" % self.format)
  456.         log.debug("format_name=%s" % self.format_name)
  457.         log.debug("last_frame=%d" % self.last_frame)
  458.         log.debug("ppl=%d" % self.pixels_per_line)
  459.         log.debug("lines=%d" % self.lines)
  460.         log.debug("depth=%d" % self.depth)
  461.         log.debug("bpl=%d" % self.bytes_per_line)
  462.         log.debug("byte_format=%s" % self.byte_format)
  463.  
  464.         w = self.buffer.write
  465.  
  466.         if self.format == scanext.FRAME_RGB: # "Color"
  467.             if self.depth == 8: # 8 bpp (32bit)
  468.                 self.pad_bytes = self.bytes_per_line - 3 * self.pixels_per_line
  469.  
  470.                 log.debug("pad_bytes=%d" % self.pad_bytes)
  471.  
  472.                 dir = -1
  473.                 if self.byte_format == 'RGBA':
  474.                     dir = 1
  475.  
  476.                 try:
  477.                     st, t = self.dev.readScan(self.bytes_per_line)
  478.                 except scanext.error, st:
  479.                     self.updateQueue(st, 0)
  480.  
  481.                 #print st
  482.                 while st == scanext.SANE_STATUS_GOOD:
  483.  
  484.                     if t:
  485.                         index = 0
  486.                         while index < len(t) - self.pad_bytes:
  487.                             w(t[index:index+3:dir])
  488.                             w('\xff')
  489.                             index += 3
  490.  
  491.                         self.total_read += len(t)
  492.                         self.updateQueue(st, self.total_read)
  493.                         log.debug("Read %d bytes" % self.total_read)
  494.  
  495.                     else:
  496.                         time.sleep(0.1)
  497.  
  498.                     try:
  499.                         st, t = self.dev.readScan(self.bytes_per_line)
  500.                     except scanext.error, st:
  501.                         self.updateQueue(st, self.total_read)
  502.                         break
  503.  
  504.                     if self.checkCancel():
  505.                         break
  506.  
  507.         elif self.format == scanext.FRAME_GRAY:
  508.  
  509.             if self.depth == 1: # 1 bpp lineart
  510.                 self.pad_bytes = self.bytes_per_line - (self.pixels_per_line + 7) // 8;
  511.  
  512.                 log.debug("pad_bytes=%d" % self.pad_bytes)
  513.  
  514.                 try:
  515.                     st, t = self.dev.readScan(self.bytes_per_line)
  516.                 except scanext.error, st:
  517.                     self.updateQueue(st, 0)
  518.  
  519.                 while st == scanext.SANE_STATUS_GOOD:
  520.  
  521.                     if t:
  522.                         index = 0
  523.                         while index < len(t) - self.pad_bytes:
  524.                             k = 0x80
  525.                             j = ord(t[index])
  526.  
  527.                             for b in range(8):
  528.                                 if k & j:
  529.                                     w("\x00\x00\x00\xff")
  530.                                 else:
  531.                                     w("\xff\xff\xff\xff")
  532.  
  533.                                 k = k >> 1
  534.  
  535.                             index += 1
  536.  
  537.                         self.total_read += len(t)
  538.                         self.updateQueue(st, self.total_read)
  539.                         log.debug("Read %d bytes" % self.total_read)
  540.                     else:
  541.                         time.sleep(0.1)
  542.  
  543.                     try:
  544.                         st, t = self.dev.readScan(self.bytes_per_line)
  545.                     except scanext.error, st:
  546.                         self.updateQueue(st, self.total_read)
  547.                         break
  548.  
  549.                     if self.checkCancel():
  550.                         break
  551.  
  552.             elif self.depth == 8: # 8 bpp grayscale
  553.                 self.pad_bytes = self.bytes_per_line - self.pixels_per_line
  554.  
  555.                 log.debug("pad_bytes=%d" % self.pad_bytes)
  556.  
  557.                 try:
  558.                     st, t = self.dev.readScan(self.bytes_per_line)
  559.                 except scanext.error, st:
  560.                     self.updateQueue(st, 0)
  561.  
  562.                 while st == scanext.SANE_STATUS_GOOD:
  563.  
  564.                     if t:
  565.                         index = 0
  566.                         while index < len(t) - self.pad_bytes:
  567.                             j = t[index]
  568.                             w(j)
  569.                             w(j)
  570.                             w(j)
  571.                             w("\xff")
  572.  
  573.                             index += 1
  574.  
  575.                         self.total_read += len(t)
  576.                         self.updateQueue(st, self.total_read)
  577.                         log.debug("Read %d bytes" % self.total_read)
  578.                     else:
  579.                         time.sleep(0.1)
  580.  
  581.                     try:
  582.                         st, t = self.dev.readScan(self.bytes_per_line)
  583.                     except scanext.error, st:
  584.                         self.updateQueue(st, self.total_read)
  585.                         break
  586.  
  587.                     if self.checkCancel():
  588.                         break
  589.  
  590.         #self.dev.cancelScan()
  591.         self.buffer.seek(0)
  592.         self.scan_active = False
  593.         log.debug("Scan thread exiting...")
  594.  
  595.  
  596.     def checkCancel(self):
  597.         canceled = False
  598.         while self.event_queue.qsize():
  599.             try:
  600.                 event = self.event_queue.get(0)
  601.                 if event == EVENT_SCAN_CANCELED:
  602.                     canceled = True
  603.                     log.debug("Cancel pressed!")
  604.                     self.dev.cancelScan()
  605.  
  606.             except Queue.Empty:
  607.                 break
  608.  
  609.         return canceled
  610.  
  611.  
  612.  
  613. def init():
  614.     return scanext.init()
  615.  
  616.  
  617. def deInit():
  618.     return scanext.deInit()
  619.  
  620.  
  621. def openDevice(dev):
  622.     "Open a device for scanning"
  623.     return ScanDevice(dev)
  624.  
  625.  
  626. def getDevices(local_only=0):
  627.     return scanext.getDevices(local_only)
  628.  
  629.  
  630. def reportError(code):
  631.     log.error("SANE: %s (code=%d)" % (scanext.getErrorMessage(code), code))
  632.  
  633.  
  634.